﻿using FastEmit;
using System.Buffers;
using System.Web;

namespace FastApi
{
    public static class FastExtensions
    {
        /// <summary>
        /// 设置客户端下载文件名
        /// </summary>
        /// <param name="response"></param>
        /// <param name="name"></param>
        public static void SetFileName(this HttpResponse response, string name = null)
        {
            if (!string.IsNullOrEmpty(name))
            {
                name = HttpUtility.UrlEncode(name);
                response.Headers.Add("Content-disposition", $"attachment;filename={name}");
            }
            response.ContentType = "application/octet-stream";
        }

        /// <summary>
        /// 输出字节到客户端
        /// </summary>
        /// <param name="response"></param>
        /// <param name="bytes"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static async Task WriteFileAsync(this HttpResponse response, ReadOnlyMemory<byte> buffer, string fileName = null)
        {
            response.ContentLength = buffer.Length;
            SetFileName(response, fileName);
            var token = response.HttpContext.RequestAborted;
            await response.Body.WriteAsync(buffer, token);
            await response.Body.FlushAsync(token);
        }

        /// <summary>
        /// 输出流到客户端
        /// </summary>
        /// <param name="response"></param>
        /// <param name="stream"></param>
        /// <param name="fileName"></param>
        /// <param name="length"></param>
        /// <param name="bufferSize">不要超过84975</param>
        /// <returns></returns>
        public static async Task WriteFileAsync(this HttpResponse response, Stream stream, string fileName = null, long? length = null, int bufferSize = 4096)
        {
            response.ContentLength = length;
            SetFileName(response, fileName);

            var buffer = ArrayPool<byte>.Shared.Rent(bufferSize); //下载缓冲
            try
            {
                int currentRead;
                var token = response.HttpContext.RequestAborted;
                do
                {
                    token.ThrowIfCancellationRequested();
                    currentRead = await stream.ReadAsync(buffer, token);
                    if (currentRead > 0)
                    {
                        await response.Body.WriteAsync(buffer.AsMemory(0, currentRead), token); //写入响应体
                        await response.Body.FlushAsync(token); //刷新响应体缓冲区，确保数据发送到客户端
                    }
                }
                while (currentRead > 0);
            }
            finally
            {
                ArrayPool<byte>.Shared.Return(buffer);
            }
        }

        /// <summary>
        /// 读取本地文件，输出到客户端
        /// </summary>
        /// <param name="response"></param>
        /// <param name="filePath"></param>
        /// <param name="fileName"></param>
        /// <param name="bufferSize"></param>
        /// <returns></returns>
        public static async Task WriteFileAsync(this HttpResponse response, string filePath, string fileName = null, int bufferSize = 4096)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                fileName = Path.GetFileName(filePath);
            }
            using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize, true);
            await WriteFileAsync(response, fs, fileName, fs.Length, bufferSize);
        }

        public static object BindModel(this IQueryCollection query, Type type)
        {
            var obj = Activator.CreateInstance(type);
            var accessor = FastEmitFactory.GetTypeAccessor(type);
            foreach (var item in accessor.MemberDict)
            {
                string val = query[item.Key];
                if (val != null)
                {
                    if (item.Value == typeof(string))
                    {
                        accessor.SetValue(obj, item.Key, val);
                    }
                    else
                    {
                        if (val != "")
                        {
                            var newVal = TypeConverter.Get(item.Value, val);
                            accessor.SetValue(obj, item.Key, newVal);
                        }
                    }
                }
            }
            return obj;
        }

        public static T BindModel<T>(this IQueryCollection query)
        {
            return (T)BindModel(query, typeof(T));
        }

        public static void BindToModel(this IQueryCollection query, object model)
        {
            var type = model.GetType();
            var accessor = FastEmitFactory.GetTypeAccessor(type);
            foreach (var item in accessor.MemberDict)
            {
                string val = query[item.Key];
                if (val != null)
                {
                    if (item.Value == typeof(string))
                    {
                        accessor.SetValue(model, item.Key, val);
                    }
                    else
                    {
                        if (val != "")
                        {
                            var newVal = TypeConverter.Get(item.Value, val);
                            accessor.SetValue(model, item.Key, newVal);
                        }
                    }
                }
            }
        }

        public static object BindModel(this IFormCollection form, Type type)
        {
            var obj = Activator.CreateInstance(type);
            var accessor = FastEmitFactory.GetTypeAccessor(type);
            foreach (var item in accessor.MemberDict)
            {
                string val = form[item.Key];
                if (val != null)
                {
                    if (item.Value == typeof(string))
                    {
                        accessor.SetValue(obj, item.Key, val);
                    }
                    else
                    {
                        if (val != "")
                        {
                            var newVal = TypeConverter.Get(item.Value, val);
                            accessor.SetValue(obj, item.Key, newVal);
                        }
                    }
                }
            }
            return obj;
        }

        public static T BindModel<T>(this IFormCollection form)
        {
            return (T)BindModel(form, typeof(T));
        }

        public static void BindToModel(this IFormCollection form, object model)
        {
            var type = model.GetType();
            var accessor = FastEmitFactory.GetTypeAccessor(type);
            foreach (var item in accessor.MemberDict)
            {
                string val = form[item.Key];
                if (val != null)
                {
                    if (item.Value == typeof(string))
                    {
                        accessor.SetValue(model, item.Key, val);
                    }
                    else
                    {
                        if (val != "")
                        {
                            var newVal = TypeConverter.Get(item.Value, val);
                            accessor.SetValue(model, item.Key, newVal);
                        }
                    }
                }
            }
        }

    }
}
